package com.jayway.opengl.tutorial;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;

public class OpenGLRenderer implements Renderer {
	float vertices[] = new float[] { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
			1.0f, 1.0f };

	FloatBuffer mVerticesBuffer;
	float mAspectRatio[] = new float[2];
	float mAspectRatioPreview[] = new float[2];

	float mTransformM[] = new float[16];
	float mTranslate[] = new float[] { 0.0f, 0.0f };
	float mScale = 1.0f;

	int mWidth;
	int mHeight;
	int mBitmapWidth;
	int mBitmapHeight;

	private int mProgramHandler;
	private int mTextureId;

	Context mContext;

	public OpenGLRenderer(Context context) {
		mContext = context;

		mVerticesBuffer = ByteBuffer.allocateDirect(vertices.length * 4)
				.order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVerticesBuffer.put(vertices).position(0);
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		GLES20.glClearColor(0, 0, 0, 1);

		int textures[] = new int[1];
		GLES20.glGenTextures(1, textures, 0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
		GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
				GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
		GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
				GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
		GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
				GLES20.GL_CLAMP_TO_EDGE);
		GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
				GLES20.GL_CLAMP_TO_EDGE);
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inPreferredConfig = Config.ARGB_8888;
		Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
				R.drawable.jay, options);
		GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
		mTextureId = textures[0];

		mBitmapWidth = bitmap.getWidth();
		mBitmapHeight = bitmap.getHeight();

		System.out.println("bitmap width = " + mBitmapWidth);
		System.out.println("bitmap height = " + mBitmapHeight);

		mAspectRatioPreview[0] = 1.0f;
		mAspectRatioPreview[1] = mBitmapWidth / mBitmapHeight;

		bitmap.recycle();

		int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		if (vertexShaderHandle != 0) {
			GLES20.glShaderSource(vertexShaderHandle,
					loadRawString(mContext, R.raw.normal_vertex));
			GLES20.glCompileShader(vertexShaderHandle);
			final int[] compileStatus = new int[1];
			GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS,
					compileStatus, 0);
			if (compileStatus[0] == 0) {
				GLES20.glDeleteShader(vertexShaderHandle);
				Log.d("OpenGLES",
						"Compilation\n"
								+ GLES20.glGetShaderInfoLog(vertexShaderHandle));
				vertexShaderHandle = 0;
			}
		}

		if (vertexShaderHandle == 0) {
			throw new RuntimeException("Error creating vertex shader.");
		}

		int fragmentShaderHandle = GLES20
				.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
		if (fragmentShaderHandle != 0) {
			GLES20.glShaderSource(fragmentShaderHandle,
					loadRawString(mContext, R.raw.normal_fragment));
			GLES20.glCompileShader(fragmentShaderHandle);
			final int[] compileStatus = new int[1];
			GLES20.glGetShaderiv(fragmentShaderHandle,
					GLES20.GL_COMPILE_STATUS, compileStatus, 0);

			if (compileStatus[0] == 0) {
				GLES20.glDeleteShader(fragmentShaderHandle);
				Log.d("OpenGLES",
						"Compilation\n"
								+ GLES20.glGetShaderInfoLog(fragmentShaderHandle));
				fragmentShaderHandle = 0;
			}
		}

		if (fragmentShaderHandle == 0) {
			throw new RuntimeException("Error creating fragment shader.");
		}

		int programHandle = GLES20.glCreateProgram();
		if (programHandle != 0) {
			GLES20.glAttachShader(programHandle, vertexShaderHandle);
			GLES20.glAttachShader(programHandle, fragmentShaderHandle);
			GLES20.glLinkProgram(programHandle);
			final int[] linkStatus = new int[1];
			GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS,
					linkStatus, 0);
			if (linkStatus[0] == 0) {
				GLES20.glDeleteProgram(programHandle);
				programHandle = 0;
			}
		}

		if (programHandle == 0) {
			throw new RuntimeException("Error creating program.");
		}

		// Tell OpenGL to use this program when rendering.
		GLES20.glUseProgram(programHandle);
		mProgramHandler = programHandle;

		Log.d("OpenGLES", "Init success.");
	}

	public void onDrawFrame(GL10 gl) {
		GLES20.glUseProgram(mProgramHandler);
		GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		int positionHandler = GLES20.glGetAttribLocation(mProgramHandler,
				"position");
		GLES20.glVertexAttribPointer(positionHandler, 2, GLES20.GL_FLOAT,
				false, 0, mVerticesBuffer);
		GLES20.glEnableVertexAttribArray(positionHandler);

		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
		GLES20.glUniform1i(GLES20.glGetUniformLocation(mProgramHandler,
				"inputImageTexture"), 0);

		GLES20.glUniform2fv(
				GLES20.glGetUniformLocation(mProgramHandler, "uAspectRatio"),
				1, mAspectRatio, 0);
		GLES20.glUniform2fv(GLES20.glGetUniformLocation(mProgramHandler,
				"uAspectRatioPreview"), 1, mAspectRatioPreview, 0);

		Matrix.setRotateM(mTransformM, 0, 180, 1f, 0f, 0f);
		GLES20.glUniformMatrix4fv(
				GLES20.glGetUniformLocation(mProgramHandler, "uTransformM"), 1,
				false, mTransformM, 0);

		GLES20.glUniform2fv(
				GLES20.glGetUniformLocation(mProgramHandler, "uTranslate"), 1,
				mTranslate, 0);
		GLES20.glUniform1f(
				GLES20.glGetUniformLocation(mProgramHandler, "uScale"), mScale);

		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
		GLES20.glDisableVertexAttribArray(positionHandler);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		GLES20.glViewport(0, 0, width, height);
		System.out.println("width = " + width + "; height = " + height);

		mWidth = width;
		mHeight = height;

		mAspectRatio[0] = (float) Math.min(width, height) / width;
		mAspectRatio[1] = (float) Math.min(width, height) / height;
	}

	public String loadRawString(Context context, int rawId) {
		String result = null;
		InputStream is = context.getResources().openRawResource(rawId);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] buf = new byte[1024];
		int len;
		try {
			while ((len = is.read(buf)) != -1) {
				baos.write(buf, 0, len);
			}
			result = baos.toString();

			is.close();
			baos.close();
		} catch (IOException e) {
		}

		return result;
	}

}
